package m.appserver;

import com.alibaba.fastjson.JSONObject;
import com.google.zxing.WriterException;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import m.common.action.Action;
import m.common.action.HtmlResult;
import m.common.action.VueResult;
import m.system.RuntimeData;
import m.system.db.TransactionManager;
import m.system.exception.MException;
import m.system.url.FilterUrl;
import m.system.url.UrlMarker;
import m.system.util.*;
import manage.run.ServerFilter;

import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import java.util.List;

public class ServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
	public static int invalidRequestCount=0;

	@Override
	protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
		Date startDate=new Date();
		String servletPath=request.uri();
		if(RuntimeData.getDebug()){
			String basePath = request.headers().get(HttpHeaderNames.HOST);
			SystemUtil.println("-----------------------------------------------------------");
			SystemUtil.println(DateUtil.format(startDate, DateUtil.YYYY_MM_DD_HH_MM_SS),"\t>>>>>\t\t","url:",basePath,servletPath);
		}
		RequestUtil reqUtil=new RequestUtil(ctx, request);
		ResponseUtil resUtil=reqUtil.getResponse();
		AuthInfoUtil.init(reqUtil);//初始化session
		if(UrlMarker.isDisabledUrl(servletPath)) {
			SystemUtil.println("Disabled url:",servletPath);
			resUtil.outTxt(HttpResponseStatus.NOT_FOUND,"无效地址");
			invalidRequestCount++;
			return;
		}
		try {
			ServerFilter sf=ServerFilter.threadInstance();
			sf.filterBefore(reqUtil, resUtil);//请求统计
			executeFilter(reqUtil,request.uri(),request.method(),null);
			sf.filterAfter();
			ServerFilter.clearInstance();
		} catch (Exception e) {
			if(e instanceof InvocationTargetException){
				Throwable target=((InvocationTargetException)e).getTargetException();
				if(target instanceof MException) ((MException) target).record();
			}else{
				if(e instanceof MException) ((MException) e).record();
			}
			Action.directLog("直接异常","channelRead0异常",reqUtil,e);
		}
		AuthInfoUtil.release();
		TransactionManager.closeConnection();
		if(RuntimeData.getDebug()){
			Date endDate=new Date();
			long time=endDate.getTime()-startDate.getTime();
			SystemUtil.print(DateUtil.format(endDate, DateUtil.YYYY_MM_DD_HH_MM_SS),"\t<<<<<\t 耗时:",time);
			if(time>1000) {
				SystemUtil.print(" 耗时超1秒 ");
			}
			SystemUtil.println("\turl:",servletPath);
		}
	}
	/**
	 * 请求过滤
	 */
	private void executeFilter(RequestUtil reqUtil, String uri, HttpMethod uriMethod, JSONObject requestBody) throws Exception {
		ResponseUtil resUtil=reqUtil.getResponse();
		String servletPath= uri.contains("?") ?uri.substring(0,uri.indexOf("?")):uri;
		if("/".equals(servletPath)) servletPath="/index.html";
		String serverName=StringUtil.noSpace(reqUtil.getHeader(HttpHeaderNames.HOST)).split(":")[0];
		String referer=reqUtil.getHeader(HttpHeaderNames.REFERER);
		String authorization=reqUtil.getHeader("Authorization");
		boolean isEnableUrl= Boolean.parseBoolean(reqUtil.getHeader("isEnableUrl"));
		if(isEnableUrl || UrlMarker.isEnableUrl(serverName, servletPath, referer, authorization)){
			reqUtil.setHeader("isEnableUrl", true);
			if(RuntimeData.getDebug()) {
				SystemUtil.println("   referer:",referer);
				SystemUtil.println("\t Authorization:",authorization);
			}
		}else {
			if(RuntimeData.getDebug()) {
				SystemUtil.println("Disabled url!");
				SystemUtil.println("\t referer:",referer);
				SystemUtil.println("\t Authorization:",authorization);
			}
			resUtil.outTxt(HttpResponseStatus.UNAUTHORIZED,"无权访问");
			invalidRequestCount++;
			return;
		}
		List<FilterUrl> filterList=UrlMarker.createFilters();
		ServerResultStatus filterStatus=ServerResultStatus.SUCCESS;
		if(UrlMarker.isFliter(filterList,reqUtil)){
			resUtil.outTxt(HttpResponseStatus.UNAUTHORIZED,"无权访问");
			invalidRequestCount++;
			return;
		}
		Object result=null;
		String redirectPath=UrlMarker.getRedirectUrl(serverName,servletPath);
		if(null!=redirectPath){
			if(redirectPath.indexOf("/")!=0) redirectPath="/"+redirectPath;
			SystemUtil.print("--------redirect:",redirectPath);
			if(redirectPath.contains("?")) {
				if(null==requestBody) requestBody=new JSONObject();
				String[] arr=redirectPath.substring(redirectPath.indexOf("?")+1).split("&");
				for(String str : arr) {
					String[] ss=str.split("=");
					ActionUtil.fillJSONObject(requestBody, ss[0], ss.length==1?"":str.substring(str.indexOf("=")+1));
				}
			}
			for(FilterUrl f : filterList) f.toResult(ServerResultStatus.REDIRECT,redirectPath);
			executeFilter(reqUtil,redirectPath,uriMethod,requestBody);
			return;
		}
		if(servletPath.length()>1){
			String[] urlParts=servletPath.substring(1).split("/");
			if("action".equals(urlParts[0])){//action请求
				String method=urlParts[urlParts.length-1];
				String[] actions=new String[urlParts.length-2];
				System.arraycopy(urlParts,1,actions,0,actions.length);
				try {
					@SuppressWarnings("unchecked")
					Class<Action> clazz=(Class<Action>) RuntimeData.getAction(ArrayUtil.connection(actions, "/"));
					if(null==clazz){
						throw new MException(this.getClass(),"没有找到Action!");
					}
					JSONObject jsonBody=reqUtil.getRequestJson();
					if(RuntimeData.getDebug()) {
						SystemUtil.println("RequestBody:",jsonBody);
					}
					if(null!=requestBody){
						ActionUtil.fillJSONObject(jsonBody,requestBody);
					}
					result=resUtil.redirect(new ActionRedirect(uriMethod,clazz,method));
					if(result instanceof Exception){
						throw (Exception)result;
					}else if(result instanceof ServerResult){
						ServerResult sr=(ServerResult) result;
						filterStatus=sr.getStatus();
					}
				} catch (Exception e) {
					String error;
					if(e instanceof InvocationTargetException){
						Throwable target=((InvocationTargetException)e).getTargetException();
						if(target instanceof MException) ((MException) target).record();
						error=target.getMessage();
					}else{
						if(e instanceof MException) ((MException) e).record();
						error=e.getMessage();
					}
					if(RuntimeData.getDebug()) {
						SystemUtil.println("--------------");
						SystemUtil.println("error:",error,"\turl:",servletPath);
					}
					JSONMessage message=new JSONMessage();
					message.setStatus(ServerResultStatus.EXCEPTION);
					message.push("msg",error);
					message.push("code",9);
					resUtil.outTxt(HttpResponseStatus.INTERNAL_SERVER_ERROR, message.toJSONString());
					if(!(result instanceof Exception)) {
						Action.directLog("直接异常","action执行异常",reqUtil, e);
					}
					for(FilterUrl f : filterList) f.toResult(message.getStatus(),message);
					return;
				}
			}else if("qrcode".equals(urlParts[0])){//请求二维码图片
				String text=reqUtil.getRequestParams().get("txt");
				try {
					BufferedImage image=ImageUtil.toQRCode(text, 300);
					resUtil.outBufferedImage(image, "PNG");
					for(FilterUrl f : filterList) f.toResult(ServerResultStatus.SUCCESS,text);
					return;
				} catch (WriterException e) {
					resUtil.outTxt(HttpResponseStatus.INTERNAL_SERVER_ERROR, e.getMessage());
					for(FilterUrl f : filterList) f.toResult(ServerResultStatus.EXCEPTION,e.getMessage());
					return;
				}
			}
		}
		if(null==result){
			resUtil.outPath(servletPath);
			result=servletPath;
		}else if(result instanceof VueResult){
			resUtil.outTxt(HttpResponseStatus.OK,((VueResult)result).toHtmlString());
		}else if(result instanceof HtmlResult){
			resUtil.outHtml(((HtmlResult)result).toHtmlString());
		}else{
			String outString;
			try {
				outString=ObjectUtil.toString(result);
			} catch (MException e) {
				JSONMessage message=new JSONMessage();
				message.push("msg", "输出错误!" + e.getMessage());
				outString=message.toJSONString();
			}
			SystemUtil.println("return Object:",outString);
			resUtil.outTxt(HttpResponseStatus.OK,outString);
		}
		reqUtil.deleteUploadFiles();
		for(FilterUrl f : filterList) f.toResult(filterStatus,result);
	}

	@Override
	public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
		super.channelReadComplete(ctx);
		ctx.flush(); // 4	
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		//SystemUtil.println("exceptionCaught");
		if(null != cause) cause.printStackTrace();
		if(null != ctx) ctx.close();
	}
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		super.channelActive(ctx);
	    String ipport=ctx.channel().remoteAddress().toString();
	    WebServer.channelMap.put(ipport, ctx);
	}
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		super.channelInactive(ctx);
	    String ipport=ctx.channel().remoteAddress().toString();
	    WebServer.channelMap.remove(ipport);
	}
	
}
